﻿using Mozhi.Abp.Domain.BasicData.Repositories;
using Mozhi.Abp.Domain.BasicData;
using mozhi.smarterp.Business.Enums;
using mozhi.smarterp.Business.Purchases.Repositories;
using System;
using System.Threading.Tasks;
using Volo.Abp.Guids;
using Volo.Abp;
using Mozhi.Abp.Domain.AuthorCenter;
using System.Linq;
using mozhi.smarterp.Business.Payments;
using System.Collections.Generic;
using mozhi.smarterp.Business.Products.Repositories;

namespace mozhi.smarterp.Business.Purchases.Services
{
    public class PurchaseOrderManager : DocumentManagerBase<PurchaseOrder, Guid>
    {
        private IGuidGenerator _guidGenerator;
        private IProductRepository _productRepository;
        private IDepartmentRepository _departmentRepository;
        private IEmployeeRepository _employeeRepository;
        private ITaxRateRepository _taxRateRepository;
        private ICurrencyRepository _currencyRepository;
        private IUnitRepository _unitRepository;
        private IProductAuxiliaryUnitRepository _productAuxiliaryUnitRepository;
        private ISupplierRepository _supplierRepository;
        private IPurchaseRequisitionOrderRepository _requisitionOrderRepository;
        private IPurchaseOrderReceiptRepository _purchaseOrderReceiptRepository;
        private PurchaseReceiptManager _purchaseReceiptManager;
        private IProductInventorySettingRepository _inventorySettingRepository;
        private IPurchaseReceiptRepository _purchaseReceiptRepository;

        public PurchaseOrderManager(IPurchaseOrderRepository repository, IGuidGenerator guidGenerator,
            IProductRepository productRepository, IDepartmentRepository departmentRepository, IEmployeeRepository employeeRepository,
            ICurrencyRepository currencyRepository, IUnitRepository unitRepository, IProductAuxiliaryUnitRepository productAuxiliaryUnitRepository,
            ISupplierRepository supplierRepository, INumberGeneratorRepository numberGeneratorRepository, ITaxRateRepository taxRateRepository,
            IPurchaseRequisitionOrderRepository requisitionOrderRepository , IPurchaseOrderReceiptRepository purchaseOrderReceiptRepository , 
            PurchaseReceiptManager purchaseReceiptManager , IProductInventorySettingRepository inventorySettingRepository ,IPurchaseReceiptRepository purchaseReceiptRepository) :
            base(repository, numberGeneratorRepository, DocumentType.PurchaseOrder)
        {
            _repository = repository;
            _guidGenerator = guidGenerator;
            _productRepository = productRepository;
            _departmentRepository = departmentRepository;
            _employeeRepository = employeeRepository;
            _currencyRepository = currencyRepository;
            _unitRepository = unitRepository;
            _productAuxiliaryUnitRepository = productAuxiliaryUnitRepository;
            _supplierRepository = supplierRepository;
            _taxRateRepository = taxRateRepository;
            _requisitionOrderRepository = requisitionOrderRepository;
            _purchaseOrderReceiptRepository = purchaseOrderReceiptRepository;
            _purchaseReceiptManager = purchaseReceiptManager;
            _inventorySettingRepository = inventorySettingRepository;
            _purchaseReceiptRepository = purchaseReceiptRepository;
        }

        public async Task<PurchaseOrder> GetAysnc(Guid? id, Guid? departmentId, DateTime date, DateTime? requiredDate, string number , string comment, PurchaseOrderType type, Guid? paymentTermId,
            Guid currencyId, decimal? exchangeRate, bool includeTax, Guid? purchaserId, Guid? supplierId)
        {
            var department = departmentId == null ? null : await _departmentRepository.GetAsync(departmentId.Value);

            var purchaser = purchaserId == null ? null : await _employeeRepository.GetAsync(purchaserId.Value);

            var currency = await _currencyRepository.GetAsync(currencyId);

            //有修改汇率，用修改的，否则用币别单据日期的汇率
            if (currency != null && exchangeRate.HasValue)
                currency.SetExchangeRate(exchangeRate.Value);

            var supplier = supplierId == null ? null : await _supplierRepository.GetAsync(supplierId.Value);

            PurchaseOrder order;

            if (id.HasValue)
            {
                order = await _repository.GetAsync(id.Value);

                if (order == null)
                    throw new UserFriendlyException($"采购订单：{id.Value}不存在", "100000");

                order.Change(order.Number, date, requiredDate, type, currency, department, purchaser, supplier, paymentTermId, includeTax, comment);
            }
            else
            {
                number = number.IsNullOrEmpty() ? await GetNextNumberAsync() : number;

                var count = await _repository.GetCountAsync(number, null);

                if (count > 0)
                    throw new UserFriendlyException($"采购订单编号：{number}已存在", "100000");

                order = new PurchaseOrder(_guidGenerator.Create(), number, date, requiredDate, type, currency, department, purchaser, supplier, paymentTermId, includeTax, comment);
                order.SetStatus(DocumentStatusType.Creation);
            }

            return order;
        }

        public async Task<PurchaseOrderItem> GetItemsAsync(Guid? id, Guid productId, Guid purchaseUnitId, decimal puchaseQuantity, decimal price, bool isGift, Guid? taxRateId, string remark)
        {
            var product = await _productRepository.GetAsync(productId);

            var purchaseUnit = await _unitRepository.GetAsync(purchaseUnitId);

            var auxiliaryUnit = await _productAuxiliaryUnitRepository.GetAsync(productId, purchaseUnitId);

            var taxrate = taxRateId.HasValue ? await _taxRateRepository.GetAsync(taxRateId.Value) : null;

            id = id ?? _guidGenerator.Create();

            var item = new PurchaseOrderItem(id.Value, product, purchaseUnit, auxiliaryUnit, puchaseQuantity, price, taxrate, isGift, remark);

            return item;
        }

        public async Task<PurchaseOrderAdvancePaymentItem> GetAdvancePaymentItemAsync(Guid? id , decimal advancePercent , DateTime? dueDate , DateTime? expectedDate)
        {
            id = id ?? _guidGenerator.Create();

            var item = new PurchaseOrderAdvancePaymentItem(id.Value, advancePercent, dueDate, expectedDate);

            return item;
        }

        public async Task DeleteAsync(Guid id)
        {
            var order = await _repository.GetAsync(id);

            if (order == null)
                throw new UserFriendlyException($"采购订单：{id}不存在", "100000");

            if (order.Status != DocumentStatusType.Creation)
                throw new UserFriendlyException("单据非创建状态，不允许删除", "100000");

            await _repository.DeleteAsync(order.Id);

            var requisitionOrders = await _requisitionOrderRepository.GetListAsync(null, null, order.Id, null);

            if (requisitionOrders.Count == 0)
                return;

            //删除掉申请单和采购订单的关系
            var requisitionOrderIds = requisitionOrders.Select(x => x.Id).ToList();

            await _requisitionOrderRepository.DeleteManyAsync(requisitionOrderIds);

        }

        public async Task<PurchaseOrder> UpdateStatusAsync(Guid id , DocumentStatusType status)
        {
            var order = await _repository.GetAsync(id , false);

            if (order == null)
                throw new UserFriendlyException($"订单：{id}不存在", "100000");

            order.SetStatus(status);

            if(status == DocumentStatusType.PendingApproval && order.Status == DocumentStatusType.Approved)
            {
                //反审核需要校验是否生成了采购入库单和预付申请单
                var orderReceiptCount = await _purchaseOrderReceiptRepository.GetCountAsync(order.Id, null, null, null);

                if(orderReceiptCount>0)
                    throw new UserFriendlyException($"采购订单:{order.Number}已经关联了入库单，不允许反审核", "100000");

                var orderPaymentRequisitionCount = await _requisitionOrderRepository.GetCountAsync(null , null , order.Id, null);

                if(orderPaymentRequisitionCount>0)
                    throw new UserFriendlyException($"采购订单:{order.Number}已经关联了付款申请单，不允许反审核", "100000");
            }

            return order;
        }

        public async Task<PaymentRequisition> PushPaymentRequisitionAsync(Guid orderId)
        {
            var order = await _repository.GetAsync(orderId);

            if (order == null)
                throw new UserFriendlyException($"采购订单:{orderId}不存在", "100000");

            if (order.Status != DocumentStatusType.Approved)
                throw new UserFriendlyException($"采购订单:{order.Number}不是审核状态，不允许下推付款申请单", "100000");

            if (order.AdvancePaymentItems == null || order.AdvancePaymentItems.Count == 0)
                throw new UserFriendlyException($"采购订单:{order.Number}没有预付款信息，不能下推付款申请单", "100000");

            var orderReceiptCount = await _purchaseOrderReceiptRepository.GetCountAsync(orderId ,null , null , null);

            if (orderReceiptCount > 0)
                throw new UserFriendlyException($"采购订单:{order.Number}已关联采购入库，不能下推付款申请单", "100000");

            var paymentRequisition = new PaymentRequisition(_guidGenerator.Create(), DateTime.UtcNow, PaymentRequisitionType.PurchaseRequisition,
                TradingPartnerType.Supplier, order.SupplierId, null, null, order.CurrencyId, order.ExchangeRate, true, DocumentStatusType.Creation, null);

            var items = new List<PaymentRequisitionItem>();

            foreach (var item in order.AdvancePaymentItems)
            {
                var paymentRequisitionItem = new PaymentRequisitionItem(_guidGenerator.Create(), DocumentType.PurchaseOrder, order.Id, item.Id,
                    item.AdvancePaymentAmountFr, item.AdvancePaymentAmount, item.AdvancePaymentAmountFr, item.AdvancePaymentAmount, item.DueDate, item.ExpectedDate, null);

                items.Add(paymentRequisitionItem);
            }

            paymentRequisition.SetItems(items);

            return paymentRequisition;
        }

        public async Task<PurchaseReceipt> PushPurchaseReceiptAsync(Guid orderId)
        {
            var order = await _repository.GetAsync(orderId);

            if (order == null)
                throw new UserFriendlyException($"采购订单{orderId}不存在", "100000");

            if (order.Status != Enums.DocumentStatusType.Approved)
                throw new UserFriendlyException($"采购订单{order.Number}非审核状态，无法下推采购入库单", "100000");

            if (order.Items == null || order.Items.Count() == 0)
                throw new UserFriendlyException($"采购订单{order.Number}没有分录数据", "100000");

            var existed‌OrderReceipts = await _purchaseOrderReceiptRepository.GetListAsync(order.Id, null, null, null);

            var receiptItems = new List<PurchaseReceiptItem>();
            var orderReceipts = new List<PurchaseOrderReceipt>();

            var receipt = await _purchaseReceiptManager.GetAsync(null, null, null, DateTime.UtcNow, "", null, order.CurrencyId, order.ExchangeRate,
               order.IncludeTax, order.DepartmentId.Value, order.PurchaserId.Value, order.SupplierId);

            foreach (var orderItem in order.Items)
            {
                var matchOrderReceipts = existed‌OrderReceipts.Where(x => x.OrderItemId == orderItem.Id).ToList();

                if (matchOrderReceipts.Count > 0)
                    continue;   //表示已经下推过了，不能在下推

                var productInventorySetting = await _inventorySettingRepository.GetByProductIdAsync(orderItem.ProductId);

                //还没有下推过
                var batchNumber = productInventorySetting.EnableBatchNumber ? DateTime.Now.ToString("yyyyMMdd") : null;
                var serialNumber = productInventorySetting.EnableSerialNumber ? DateTime.Now.ToString("yyyyMMdd") : null;
                DateTime? productionDate = productInventorySetting.EnableShelfLife ? DateTime.Now : null;

                if (productInventorySetting.EnableSerialNumber)
                {
                    for (int i = 0; i < orderItem.BasicQuantity; i++)
                    {
                        //应收数量需要换算一下
                        var auxiliaryQuantity = 1 / orderItem.ConversionRate;

                        var receiptItem = await _purchaseReceiptManager.GetItemAsync(null, orderItem.ProductId, productInventorySetting.DefaultWarehouseId, productInventorySetting.DefaultLocationId, orderItem.AuxiliaryUnitId, auxiliaryQuantity,
                      orderItem.BasicQuantity, 1, orderItem.Price, orderItem.TaxRateId, batchNumber, serialNumber, productionDate, orderItem.IsGift, "");

                        receiptItems.Add(receiptItem);

                        var orderReceipt = new PurchaseOrderReceipt(_guidGenerator.Create(), orderId, orderItem.Id, receipt.Id, receiptItem.Id);
                        orderReceipts.Add(orderReceipt);
                    }
                }
                else
                {
                    var receiptItem = await _purchaseReceiptManager.GetItemAsync(null, orderItem.ProductId, productInventorySetting.DefaultWarehouseId, productInventorySetting.DefaultLocationId, orderItem.AuxiliaryUnitId, orderItem.AuxiliaryQuantity,
                     orderItem.BasicQuantity, orderItem.BasicQuantity, orderItem.Price, orderItem.TaxRateId, batchNumber, serialNumber, productionDate, orderItem.IsGift, "");

                    receiptItems.Add(receiptItem);

                    var orderReceipt = new PurchaseOrderReceipt(_guidGenerator.Create(), orderId, orderItem.Id, receipt.Id, receiptItem.Id);
                    orderReceipts.Add(orderReceipt);
                }
            }
            receipt.ChangeItems(receiptItems);

            await _purchaseReceiptRepository.InsertAsync(receipt);

            await _purchaseOrderReceiptRepository.InsertManyAsync(orderReceipts);

            return receipt;
        }
    }
}
